﻿using System;
using System.Collections.Generic;
using System.Data;
using System.Linq;
using System.Linq.Expressions;
using NetSurveillance.WebApp.DataServices.Models;
using ServiceStack.DataAnnotations;
using ServiceStack.OrmLite;
using ServiceStack.OrmLite.PostgreSQL;

namespace NetSurveillance.WebApp.DataServices.Implementations
{
    public class BaseService<T> : IServices.IBaseService<T> where T : BaseModel, new()
    {
        protected OrmLiteConnectionFactory DbFactory;

        public BaseService(string connStr, IOrmLiteDialectProvider provider = null)
        {
            if (provider == null)
                provider = PostgreSQLDialectProvider.Instance;
            DbFactory = new OrmLiteConnectionFactory(connStr, provider);
        }

        public virtual T GetModelById(long id)
        {
            T model;
            using (var db = DbFactory.OpenDbConnection())
            {
                model = db.IdOrDefault<T>(id);
            }
            return model;
        }

        public virtual IList<T> GetModels(long[] ids)
        {
            using (var db = DbFactory.OpenDbConnection())
            {
                var models = db.GetByIds<T>(ids);
                return models;
            }
        } 

        public virtual IEnumerable<T> GetAllModels(bool ignoreDeleted = false,
                                                   Expression<Func<T, bool>> predicate = null)
        {
            if (predicate == null)
                predicate = arg => true;

            using (var db = DbFactory.OpenDbConnection())
            {
                var listResult = ignoreDeleted
                                     ? db.Select(predicate)
                                     : db.Select(predicate.And(p => p.StatusToken != 0));
                return listResult ?? Enumerable.Empty<T>();
            }
        }

        public virtual ServiceResult Delete(long id)
        {
            using (var db = DbFactory.OpenDbConnection())
            {
                var model = db.IdOrDefault<T>(id);
                if (model == null) return new ServiceResult { ExecutedSuccess = false, Message = "该条数据不存在数据库" };
                model.StatusToken = 0;
                db.Save(model);
            }
            return ServiceResult.SuccessResult;
        }

        public virtual ServiceResult Save(T model)
        {
            using (var db = DbFactory.OpenDbConnection())
            {
                db.Save(model);
                TrySetAutoIncrementId(model, db);
            }
            return ServiceResult.SuccessResult;
        }

        public virtual ServiceResult SaveAll(IEnumerable<T> models)
        {
            using (var db = DbFactory.OpenDbConnection())
            {
                db.SaveAll(models);
            }
            return ServiceResult.SuccessResult;
        }

        public virtual ServiceResult UpdateOnly(T model, Expression<Func<T, dynamic>> updateFields,
                                                Expression<Func<T, bool>> wherePredicate)
        {
            using (var db = DbFactory.OpenDbConnection())
            {
                return db.UpdateOnly(model, updateFields, wherePredicate) > 0
                           ? ServiceResult.SuccessResult
                           : ServiceResult.UpdateFailureResult;
            }
        }

        public virtual ServiceResult UpdateNonDefaults(T model,
                                                       Expression<Func<T, bool>> wherePredicate)
        {
            using (var db = DbFactory.OpenDbConnection())
            {
                return db.UpdateNonDefaults(model, wherePredicate) > 0
                           ? ServiceResult.SuccessResult
                           : ServiceResult.UpdateFailureResult;
            }
        }

        protected string GetTableName()
        {
            Type modelType = typeof(T);
            var aliasAttrs = modelType.GetCustomAttributes(typeof(AliasAttribute), false);
            var aliasAttr = aliasAttrs.Length == 0 ? null : aliasAttrs[0] as AliasAttribute;
            return aliasAttr == null ? modelType.Name : aliasAttr.Name;
        }

        protected void TrySetAutoIncrementId(T modelObj, IDbConnection db)
        {
            if (modelObj == null || db == null) return;
            Type modelType = typeof(T);
            var idProperty = modelType.GetProperties().FirstOrDefault(p => p != null &&
                                                                           p.GetCustomAttributes(typeof(AutoIncrementAttribute), false)
                                                                            .Length > 0);
            if (idProperty == null) return;
            long? modelId = idProperty.GetValue(modelObj, null) as long?;
            if (!modelId.HasValue || modelId == 0)
                idProperty.SetValue(modelObj, db.GetLastInsertId(), null);
        }
    }
}